/*!
 * Danmuku.js v0.1.5
 * (c) 2019-2020 Imtaotao
 * Released under the MIT License.
 */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global.Danmuku = factory());
}(this, function () { 'use strict';

  function Timeline(manager) {
    var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    var _manager$opts = manager.opts,
        limit = _manager$opts.limit,
        forceRender = _manager$opts.forceRender;

    if (opts.forceRender) {
      manager.setOptions({
        limit: Infinity,
        forceRender: true
      });
    }

    return {
      preEmiter: null,
      timeStore: {},
      specialTimeStore: {},
      add: function add(timestamp, cfg, hooks, isForward) {
        if (!this.timeStore[timestamp]) {
          this.timeStore[timestamp] = [{
            cfg: cfg,
            hooks: hooks,
            isForward: isForward
          }];
        } else {
          this.timeStore[timestamp].push({
            cfg: cfg,
            hooks: hooks,
            isForward: isForward
          });
        }
      },
      addSpecial: function addSpecial(timestamp, cfg) {
        if (!this.specialTimeStore[timestamp]) {
          this.specialTimeStore[timestamp] = [cfg];
        } else {
          this.specialTimeStore[timestamp].push(cfg);
        }
      },
      emit: function emit(timestamp, clearOld) {
        var ordinaryData = this.timeStore[timestamp];
        var specialData = this.specialTimeStore[timestamp];

        if (ordinaryData) {
          ordinaryData.forEach(function (_ref) {
            var cfg = _ref.cfg,
                hooks = _ref.hooks,
                isForward = _ref.isForward;
            manager.send(cfg, hooks, isForward);
          });
        }

        if (specialData) {
          manager.sendSpecial(specialData);
        }

        if (clearOld) {
          var clear = function clear(data) {
            var keys = Object.keys(data);

            if (keys.length > 0) {
              for (var i = 0; i < keys.length; i++) {
                var time = keys[i];

                if (time < timestamp && data[time]) {
                  delete data[time];
                }
              }
            }
          };

          clear(this.timeStore);
          clear(this.specialTimeStore);
        }
      },
      emitInterval: function emitInterval(timestamp, clearOld) {
        if (timestamp !== preEmiter) {
          this.preEmiter = timestamp;
          this.emit(timestamp, clearOld);
        }
      },
      destroy: function destroy() {
        this.preEmiter = null;
        this.timeStore = {};
        this.specialTimeStore = {};
        manager.setOptions({
          limit: limit,
          forceRender: forceRender
        });
      }
    };
  }

  function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  }

  function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }

  function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
  }

  function _slicedToArray(arr, i) {
    return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
  }

  function _toArray(arr) {
    return _arrayWithHoles(arr) || _iterableToArray(arr) || _nonIterableRest();
  }

  function _arrayWithHoles(arr) {
    if (Array.isArray(arr)) return arr;
  }

  function _iterableToArray(iter) {
    if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
  }

  function _iterableToArrayLimit(arr, i) {
    var _arr = [];
    var _n = true;
    var _d = false;
    var _e = undefined;

    try {
      for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
        _arr.push(_s.value);

        if (i && _arr.length === i) break;
      }
    } catch (err) {
      _d = true;
      _e = err;
    } finally {
      try {
        if (!_n && _i["return"] != null) _i["return"]();
      } finally {
        if (_d) throw _e;
      }
    }

    return _arr;
  }

  function _nonIterableRest() {
    throw new TypeError("Invalid attempt to destructure non-iterable instance");
  }

  function warning(condition, message) {
    if (condition) return;
    throw new Error("Warning: ".concat(message));
  }
  function callHook(hooks, name) {
    var args = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];

    if (hooks && typeof hooks[name] === 'function') {
      return hooks[name].apply(null, args);
    }

    return null;
  }
  function createKey() {
    return Math.random().toString(36).substr(2, 8);
  }
  function toNumber(val) {
    return typeof val === 'number' ? val : typeof val === 'string' ? Number(val.replace('px', '')) : NaN;
  }
  function isRange(_ref, val) {
    var _ref2 = _slicedToArray(_ref, 2),
        a = _ref2[0],
        b = _ref2[1];

    if (val === a || val === b) return true;
    var min = Math.min(a, b);
    var max = min === a ? b : a;
    return min < val && val < max;
  }
  function upperCase(_ref3) {
    var _ref4 = _toArray(_ref3),
        first = _ref4[0],
        remaing = _ref4.slice(1);

    return first.toUpperCase() + remaing.join('');
  }
  function timeSlice(len, fn) {
    var i = -1;
    var start = performance.now();

    var run = function run() {
      while (++i < len) {
        if (fn() === false) {
          break;
        }

        var cur = performance.now();

        if (cur - start > 13) {
          start = cur;
          setTimeout(run);
          break;
        }
      }
    };

    run();
  }
  var raf = window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : setTimeout;
  function nextFrame(fn) {
    raf(function () {
      raf(fn);
    });
  }
  var transitionProp = 'transition';
  var transitionEndEvent = 'transitionend';
  var transitionDuration = 'transitionDuration';

  if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) {
    transitionProp = 'WebkitTransition';
    transitionEndEvent = 'webkitTransitionEnd';
    transitionDuration = 'webkitTransitionDuration';
  }

  function whenTransitionEnds(node) {
    return new Promise(function (resolve) {
      var isCalled = false;

      var end = function end() {
        node.removeEventListener(transitionEndEvent, onEnd);
        resolve();
      };

      var onEnd = function onEnd() {
        if (!isCalled) {
          isCalled = true;
          end();
        }
      };

      node.addEventListener(transitionEndEvent, onEnd);
    });
  }

  var Barrage =
  /*#__PURE__*/
  function () {
    function Barrage(itemData, hooks, time, manager, globalHooks) {
      _classCallCheck(this, Barrage);

      var RuntimeManager = manager.RuntimeManager;
      var _manager$opts = manager.opts,
          direction = _manager$opts.direction,
          container = _manager$opts.container;
      this.node = null;
      this.hooks = hooks;
      this.paused = false;
      this.moveing = false;
      this.data = itemData;
      this.duration = time;
      this.isSpecial = false;
      this.trajectory = null;
      this.manager = manager;
      this.direction = direction;
      this.container = container;
      this.isChangeDuration = false;
      this.globalHooks = globalHooks;
      this.RuntimeManager = RuntimeManager;
      this.key = itemData.key || createKey();
      this.position = {
        y: null
      };
      this.timeInfo = {
        pauseTime: 0,
        startTime: null,
        prevPauseTime: null,
        currentDuration: time
      };
      this.create();
    }

    _createClass(Barrage, [{
      key: "getMovePercent",
      value: function getMovePercent() {
        var _this$timeInfo = this.timeInfo,
            pauseTime = _this$timeInfo.pauseTime,
            startTime = _this$timeInfo.startTime,
            prevPauseTime = _this$timeInfo.prevPauseTime;
        var currentTime = this.paused ? prevPauseTime : Date.now();
        return (currentTime - startTime - pauseTime) / 1000 / this.duration;
      }
    }, {
      key: "getMoveDistance",
      value: function getMoveDistance() {
        if (!this.moveing) return 0;
        var percent = this.getMovePercent();
        var containerWidth = this.RuntimeManager.containerWidth + this.getWidth();
        return percent * containerWidth;
      }
    }, {
      key: "getHeight",
      value: function getHeight() {
        return this.node && this.node.clientHeight || 0;
      }
    }, {
      key: "getWidth",
      value: function getWidth() {
        return this.node && this.node.clientWidth || 0;
      }
    }, {
      key: "getSpeed",
      value: function getSpeed() {
        var duration = this.timeInfo.currentDuration;
        var containerWidth = this.RuntimeManager.containerWidth + this.getWidth();
        return duration == null || containerWidth == null ? 0 : containerWidth / duration;
      }
    }, {
      key: "create",
      value: function create() {
        this.node = document.createElement('div');
        callHook(this.hooks, 'create', [this, this.node]);
        callHook(this.globalHooks, 'barrageCreate', [this, this.node]);
      }
    }, {
      key: "append",
      value: function append() {
        warning(this.container, 'Need container element.');

        if (this.node) {
          this.container.appendChild(this.node);
          callHook(this.hooks, 'append', [this, this.node]);
          callHook(this.globalHooks, 'barrageAppend', [this, this.node]);
        }
      }
    }, {
      key: "remove",
      value: function remove(noCallHook) {
        warning(this.container, 'Need container element.');

        if (this.node) {
          this.container.removeChild(this.node);

          if (!noCallHook) {
            callHook(this.hooks, 'remove', [this, this.node]);
            callHook(this.globalHooks, 'barrageRemove', [this, this.node]);
          }
        }
      }
    }, {
      key: "deletedInMemory",
      value: function deletedInMemory() {
        var index = -1;
        var trajectory = this.trajectory;
        var showBarrages = this.manager.showBarrages;

        if (trajectory && trajectory.values.length > 0) {
          index = trajectory.values.indexOf(this);
          if (~index) trajectory.values.splice(index, 1);
        }

        if (showBarrages && showBarrages.length > 0) {
          index = showBarrages.indexOf(this);
          if (~index) showBarrages.splice(index, 1);
        }
      }
    }, {
      key: "destroy",
      value: function destroy() {
        this.remove();
        this.moveing = false;
        this.deletedInMemory();
        callHook(this.hooks, 'destroy', [this, this.node]);
        callHook(this.globalHooks, 'barrageDestroy', [this, this.node]);
        this.node = null;
      }
    }, {
      key: "pause",
      value: function pause() {
        if (!this.moveing || this.paused) return;
        var moveDistance = this.getMoveDistance();

        if (!Number.isNaN(moveDistance)) {
          this.paused = true;
          this.timeInfo.prevPauseTime = Date.now();

          if (this.direction === 'right') {
            moveDistance *= -1;
          }

          this.node.style[transitionDuration] = '0s';
          this.node.style.transform = "translateX(".concat(moveDistance, "px)");
        }
      }
    }, {
      key: "resume",
      value: function resume() {
        if (!this.moveing || !this.paused) return;
        this.paused = false;
        this.timeInfo.pauseTime += Date.now() - this.timeInfo.prevPauseTime;
        this.timeInfo.prevPauseTime = null;
        var isNegative = this.direction === 'left' ? 1 : -1;
        var containerWidth = this.RuntimeManager.containerWidth + this.getWidth();
        var remainingTime = (1 - this.getMovePercent()) * this.duration;
        this.timeInfo.currentDuration = remainingTime;
        this.node.style[transitionDuration] = "".concat(remainingTime, "s");
        this.node.style.transform = "translateX(".concat(containerWidth * isNegative, "px)");
      }
    }, {
      key: "reset",
      value: function reset() {
        this.remove(true);
        this.deletedInMemory();
        this.paused = false;
        this.moveing = false;
        this.trajectory = null;
        this.position = {
          y: null
        };
        this.timeInfo = {
          pauseTime: 0,
          startTime: null,
          prevPauseTime: null,
          currentDuration: this.duration
        };
      }
    }]);

    return Barrage;
  }();

  var RuntimeManager =
  /*#__PURE__*/
  function () {
    function RuntimeManager(opts) {
      _classCallCheck(this, RuntimeManager);
      var container = opts.container,
          rowGap = opts.rowGap,
          height = opts.height;
      var styles = getComputedStyle(container);

      if (!styles.position || styles.position === 'none' || styles.position === 'static') {
        container.style.position = 'relative';
      }

      this.opts = opts;
      this.rowGap = rowGap;
      this.singleHeight = height;
      this.containerElement = container;
      this.containerWidth = toNumber(styles.width);
      this.containerHeight = toNumber(styles.height);
      this.init();
    }

    _createClass(RuntimeManager, [{
      key: "init",
      value: function init() {
        this.container = [];
        this.rows = parseInt(this.containerHeight / this.singleHeight);

        for (var i = 0; i < this.rows; i++) {
          var start = this.singleHeight * i;
          var end = this.singleHeight * (i + 1) - 1;
          this.container.push({
            values: [],
            gaps: [start, end]
          });
        }
      }
    }, {
      key: "resize",
      value: function resize() {
        var styles = getComputedStyle(this.containerElement);
        this.containerWidth = toNumber(styles.width);
        this.containerHeight = toNumber(styles.height);
        this.rows = parseInt(this.containerHeight / this.singleHeight);
        var container = [];

        for (var i = 0; i < this.rows; i++) {
          var start = this.singleHeight * i;
          var end = this.singleHeight * (i + 1) - 1;
          var gaps = [start, end];

          if (this.container[i]) {
            this.container[i].gaps = gaps;
            container.push(this.container[i]);
          } else {
            container.push({
              gaps: gaps,
              values: []
            });
          }
        }

        this.container = container;
      }
    }, {
      key: "getLastBarrage",
      value: function getLastBarrage(barrages, lastIndex) {
        for (var i = barrages.length - 1; i >= 0; i--) {
          var barrage = barrages[i - lastIndex];

          if (barrage && !barrage.paused) {
            return barrage;
          }
        }

        return null;
      }
    }, {
      key: "getRandomIndex",
      value: function getRandomIndex(exclude) {
        var randomIndex = Math.floor(Math.random() * this.rows);
        return exclude.includes(randomIndex) ? this.getRandomIndex(exclude) : randomIndex;
      }
    }, {
      key: "getTrajectory",
      value: function getTrajectory() {
        var alreadyFound = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];

        if (alreadyFound.length === this.container.length) {
          if (this.opts.forceRender) {
            var _index = Math.floor(Math.random() * this.rows);

            return this.container[_index];
          } else {
            return null;
          }
        }

        var index = this.getRandomIndex(alreadyFound);
        var currentTrajectory = this.container[index];
        var lastBarrage = this.getLastBarrage(currentTrajectory.values, 0);

        if (this.rowGap <= 0 || !lastBarrage) {
          return currentTrajectory;
        }

        alreadyFound.push(index);

        if (lastBarrage.moveing) {
          var distance = lastBarrage.getMoveDistance();
          var spacing = this.rowGap > 0 ? this.rowGap + lastBarrage.getWidth() : this.rowGap;
          return distance > spacing ? currentTrajectory : this.getTrajectory(alreadyFound);
        }

        return this.getTrajectory(alreadyFound);
      }
    }, {
      key: "computingDuration",
      value: function computingDuration(prevBarrage, currentBarrage) {
        var prevWidth = prevBarrage.getWidth();
        var currentWidth = currentBarrage.getWidth();
        var prevSpeed = prevBarrage.getSpeed();
        var currentSpeed = currentBarrage.getSpeed();
        var acceleration = currentSpeed - prevSpeed;

        if (acceleration <= 0) {
          return null;
        }

        var distance = prevBarrage.getMoveDistance() - currentWidth - prevWidth;
        var meetTime = distance / acceleration;

        if (meetTime >= currentBarrage.duration) {
          return null;
        }

        var remainingTime = (1 - prevBarrage.getMovePercent()) * prevBarrage.duration;
        var currentFixTime = currentWidth * remainingTime / this.containerWidth;
        return remainingTime + currentFixTime;
      }
    }, {
      key: "move",
      value: function move(barrage, manager) {
        var _this = this;

        var node = barrage.node;
        var prevBarrage = this.getLastBarrage(barrage.trajectory.values, 1);
        node.style.top = "".concat(barrage.position.y, "px");
        return new Promise(function (resolve) {
          nextFrame(function () {
            var width = barrage.getWidth();
            var isNegative = barrage.direction === 'left' ? 1 : -1;
            var containerWidth = _this.containerWidth + width;

            if (prevBarrage && _this.rowGap > 0 && prevBarrage.moveing && !prevBarrage.paused) {
              var fixTime = _this.computingDuration(prevBarrage, barrage);

              if (fixTime !== null) {
                if (isRange(_this.opts.times, fixTime)) {
                  barrage.duration = fixTime;
                  barrage.isChangeDuration = true;
                  barrage.timeInfo.currentDuration = fixTime;
                } else {
                  barrage.reset();
                  node.style.top = null;
                  manager.stashBarrages.unshift(barrage);
                  return;
                }
              }
            }

            node.style.opacity = 1;
            node.style.pointerEvents = manager.isShow ? 'auto' : 'none';
            node.style.visibility = manager.isShow ? 'visible' : 'hidden';
            node.style.transform = "translateX(".concat(isNegative * containerWidth, "px)");
            node.style[transitionProp] = "transform linear ".concat(barrage.duration, "s");
            node.style["margin".concat(upperCase(barrage.direction))] = "-".concat(width, "px");
            barrage.moveing = true;
            barrage.timeInfo.startTime = Date.now();
            callHook(barrage.hooks, 'move', [barrage, node]);
            callHook(barrage.globalHooks, 'barrageMove', [barrage, node]);
            resolve(whenTransitionEnds(node));
          });
        });
      }
    }, {
      key: "moveSpecialBarrage",
      value: function moveSpecialBarrage(barrage, manager) {
        var _this2 = this;

        var node = barrage.node,
            opts = barrage.opts;
        node.style.position = 'absolute';
        node.style.display = 'inline-block';
        node.style.pointerEvents = manager.isShow ? 'auto' : 'none';
        node.style.visibility = manager.isShow ? 'visible' : 'hidden';
        return new Promise(function (resolve) {
          var _opts$position = opts.position(barrage),
              _opts$position$x = _opts$position.x,
              x = _opts$position$x === void 0 ? 0 : _opts$position$x,
              _opts$position$y = _opts$position.y,
              y = _opts$position$y === void 0 ? 0 : _opts$position$y;

          var setStyle = function setStyle(a, b) {
            return "translateX(".concat(a, "px) translateY(").concat(b, "px)");
          };

          node.style.transform = setStyle(x, y);
          nextFrame(function () {
            barrage.moveing = true;
            barrage.timeInfo.startTime = Date.now();
            barrage.startPosition = {
              x: x,
              y: y
            };

            if (opts.direction === 'none') {
              var fn = function fn() {
                barrage.moveTimer.clear();
                barrage.moveTimer = null;
                resolve();
              };

              var timer = setTimeout(fn, opts.duration * 1000);
              barrage.moveTimer = {
                callback: fn,
                clear: function clear() {
                  clearTimeout(timer);
                  timer = null;
                }
              };
            } else {
              var endPosition = opts.direction === 'left' ? _this2.containerWidth : -barrage.getWidth();
              node.style.transform = setStyle(endPosition, y);
              node.style[transitionProp] = "transform linear ".concat(opts.duration, "s");
              resolve(whenTransitionEnds(node));
            }

            callHook(barrage.hooks, 'move', [barrage, node]);
            callHook(manager.opts.hooks, 'barrageMove', [barrage, node]);
          });
        });
      }
    }]);

    return RuntimeManager;
  }();

  var SpecialBarrage =
  /*#__PURE__*/
  function () {
    function SpecialBarrage(manager, opts) {
      _classCallCheck(this, SpecialBarrage);

      this.opts = opts;
      this.node = null;
      this.paused = false;
      this.moveing = false;
      this.isSpecial = true;
      this.manager = manager;
      this.container = manager.opts.container;
      this.RuntimeManager = manager.RuntimeManager;
      this.hooks = opts.hooks;
      this.data = opts.data || null;
      this.key = opts.key || createKey();
      this.moveTimer = null;
      this.timeInfo = {
        pauseTime: 0,
        startTime: null,
        prevPauseTime: null,
        currentDuration: opts.duration
      };
      this.startPosition = {
        x: null,
        y: null
      };
    }

    _createClass(SpecialBarrage, [{
      key: "getHeight",
      value: function getHeight() {
        return this.node && this.node.clientHeight || 0;
      }
    }, {
      key: "getWidth",
      value: function getWidth() {
        return this.node && this.node.clientWidth || 0;
      }
    }, {
      key: "create",
      value: function create() {
        this.node = document.createElement('div');
        callHook(this.hooks, 'create', [this, this.node]);
        callHook(this.manager.opts.hooks, 'barrageCreate', [this, this.node]);
      }
    }, {
      key: "getMovePercent",
      value: function getMovePercent() {
        var _this$timeInfo = this.timeInfo,
            pauseTime = _this$timeInfo.pauseTime,
            startTime = _this$timeInfo.startTime,
            prevPauseTime = _this$timeInfo.prevPauseTime;
        var currentTime = this.paused ? prevPauseTime : Date.now();
        return (currentTime - startTime - pauseTime) / 1000 / this.opts.duration;
      }
    }, {
      key: "getMoveDistance",
      value: function getMoveDistance(direction, startPosition) {
        if (!this.moveing) return 0;
        var percent = this.getMovePercent();

        if (direction === 'none') {
          return startPosition;
        }

        if (direction === 'left') {
          var realMoveDistance = (this.RuntimeManager.containerWidth - startPosition) * percent;
          return startPosition + realMoveDistance;
        } else {
          var allMoveDistance = startPosition + this.getWidth();
          return startPosition - allMoveDistance * percent;
        }
      }
    }, {
      key: "pause",
      value: function pause() {
        if (!this.moveing || this.paused) return;
        this.paused = true;
        this.timeInfo.prevPauseTime = Date.now();
        var direction = this.opts.direction;

        if (direction === 'none') {
          if (this.moveTimer) {
            this.moveTimer.clear();
          }
        } else {
          var _this$startPosition = this.startPosition,
              x = _this$startPosition.x,
              y = _this$startPosition.y;
          var moveDistance = this.getMoveDistance(direction, x);
          this.node.style[transitionDuration] = '0s';
          this.node.style.transform = "translateX(".concat(moveDistance, "px) translateY(").concat(y, "px)");
        }
      }
    }, {
      key: "resume",
      value: function resume() {
        if (!this.moveing || !this.paused) return;
        this.paused = false;
        this.timeInfo.pauseTime += Date.now() - this.timeInfo.prevPauseTime;
        this.timeInfo.prevPauseTime = null;
        var direction = this.opts.direction;
        var remainingTime = (1 - this.getMovePercent()) * this.opts.duration;

        if (direction === 'none') {
          var fn = this.moveTimer.callback || function () {};

          var timer = setTimeout(fn, remainingTime * 1000);

          this.moveTimer.clear = function () {
            clearTimeout(timer);
            timer = null;
          };
        } else {
          var _this$startPosition2 = this.startPosition,
              x = _this$startPosition2.x,
              y = _this$startPosition2.y;
          var endPosition = this.opts.direction === 'left' ? this.RuntimeManager.containerWidth : -this.getWidth();
          this.node.style[transitionDuration] = "".concat(remainingTime, "s");
          this.node.style.transform = "translateX(".concat(endPosition, "px) translateY(").concat(y, "px)");
        }
      }
    }, {
      key: "append",
      value: function append() {
        warning(this.container, 'Need container element.');

        if (this.node) {
          this.container.appendChild(this.node);
          callHook(this.hooks, 'append', [this, this.node]);
          callHook(this.manager.opts.hooks, 'barrageAppend', [this, this.node]);
        }
      }
    }, {
      key: "remove",
      value: function remove() {
        warning(this.container, 'Need container element.');

        if (this.node) {
          this.container.removeChild(this.node);
          callHook(this.hooks, 'remove', [this, this.node]);
          callHook(this.manager.opts.hooks, 'barrageRemove', [this, this.node]);
        }
      }
    }, {
      key: "destroy",
      value: function destroy() {
        this.remove();
        this.moveing = false;
        var index = this.manager.specialBarrages.indexOf(this);

        if (~index) {
          this.manager.specialBarrages.splice(index, 1);
        }

        if (this.moveTimer) {
          this.moveTimer.clear();
          this.moveTimer = null;
        }

        callHook(this.hooks, 'destroy', [this, this.node]);
        callHook(this.manager.opts.hooks, 'barrageDestroy', [this, this.node]);
        this.node = null;
      }
    }]);

    return SpecialBarrage;
  }();
  function createSpecialBarrage(manager) {
    var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    opts = Object.assign({
      hooks: {},
      duration: 0,
      direction: 'none',
      position: function position() {
        return {
          x: 0,
          y: 0
        };
      }
    }, opts);
    return new SpecialBarrage(manager, opts);
  }

  var BarrageManager =
  /*#__PURE__*/
  function () {
    function BarrageManager(opts) {
      _classCallCheck(this, BarrageManager);

      this.opts = opts;
      this.loopTimer = null;
      this.plugins = new Map();
      this.showBarrages = [];
      this.stashBarrages = [];
      this.specialBarrages = [];
      this.isShow = opts.isShow;
      this.container = opts.container;
      this.RuntimeManager = new RuntimeManager(opts);
    }

    _createClass(BarrageManager, [{
      key: "send",
      value: function send(data, hooks, isForward) {
        if (Array.isArray(data)) {
          data = data.map(function (item) {
            return {
              data: item,
              hooks: hooks
            };
          });
        } else {
          data = [{
            data: data,
            hooks: hooks
          }];
        }

        if (this.assertCapacity(data.length)) return false;
        isForward ? this.stashBarrages.unshift.apply(this.stashBarrages, data) : this.stashBarrages.push.apply(this.stashBarrages, data);
        callHook(this.opts.hooks, 'send', [this, data]);
        return true;
      }
    }, {
      key: "sendSpecial",
      value: function sendSpecial(data) {
        var _this = this;

        if (!this.runing) return false;
        if (!Array.isArray(data)) data = [data];
        if (this.assertCapacity(data.length)) return false;

        for (var i = 0; i < data.length; i++) {
          if (callHook(this.opts.hooks, 'willRender', [this, data[i], true]) !== false) {
            var _ret = function () {
              var barrage = createSpecialBarrage(_this, data[i]);

              if (barrage.opts.duration <= 0 || _this.showLength + 1 > _this.opts.limit) {
                return "continue";
              }

              barrage.create();
              barrage.append();

              _this.specialBarrages.push(barrage);

              _this.RuntimeManager.moveSpecialBarrage(barrage, _this).then(function () {
                barrage.destroy();

                if (_this.length === 0) {
                  callHook(_this.opts.hooks, 'ended', [_this]);
                }
              });
            }();

            if (_ret === "continue") continue;
          }
        }

        callHook(this.opts.hooks, 'sendSpecial', [this, data]);
        return true;
      }
    }, {
      key: "show",
      value: function show() {
        if (!this.isShow) {
          this.isShow = true;
          this.each(function (barrage) {
            if (barrage.node) {
              barrage.node.style.visibility = 'visible';
              barrage.node.style.pointerEvents = 'auto';
            }

            callHook(barrage.hooks, 'show', [barrage, barrage.node]);
          });
          callHook(this.opts.hooks, 'show', [this]);
        }
      }
    }, {
      key: "hidden",
      value: function hidden() {
        if (this.isShow) {
          this.isShow = false;
          this.each(function (barrage) {
            if (barrage.node) {
              barrage.node.style.visibility = 'hidden';
              barrage.node.style.pointerEvents = 'none';
            }

            callHook(barrage.hooks, 'hidden', [barrage, barrage.node]);
          });
          callHook(this.opts.hooks, 'hidden', [this]);
        }
      }
    }, {
      key: "each",
      value: function each(callback) {
        if (typeof callback === 'function') {
          var i = 0;

          for (; i < this.specialBarrages.length; i++) {
            callback(this.specialBarrages[i], i);
          }

          for (i = 0; i < this.showBarrages.length; i++) {
            var barrage = this.showBarrages[i];

            if (barrage.moveing) {
              callback(barrage, i);
            }
          }
        }
      }
    }, {
      key: "stop",
      value: function stop(noCallHook) {
        if (this.loopTimer) {
          clearTimeout(this.loopTimer);
          this.loopTimer = null;

          if (!noCallHook) {
            callHook(this.opts.hooks, 'stop', [this]);
          }
        }
      }
    }, {
      key: "start",
      value: function start(noCallHook) {
        var _this2 = this;

        var core = function core() {
          _this2.loopTimer = setTimeout(function () {
            _this2.renderBarrage();

            core();
          }, _this2.opts.interval * 1000);
        };

        this.stop(true);
        core();

        if (!noCallHook) {
          callHook(this.opts.hooks, 'start', [this]);
        }
      }
    }, {
      key: "setOptions",
      value: function setOptions(opts) {
        if (opts) {
          this.opts = Object.assign(this.opts, opts);

          if ('interval' in opts) {
            this.stop(true);
            this.start(true);
          }

          if ('height' in opts) {
            this.RuntimeManager.singleHeight = opts.height;
            this.RuntimeManager.resize();
          }

          if ('rowGap' in opts) {
            this.RuntimeManager.rowGap = opts.rowGap;
          }

          callHook(this.opts.hooks, 'setOptions', [this, opts]);
        }
      }
    }, {
      key: "resize",
      value: function resize() {
        this.RuntimeManager.resize();
        callHook(this.opts.hooks, 'resize', [this]);
      }
    }, {
      key: "clear",
      value: function clear() {
        this.stop();
        this.each(function (barrage) {
          return barrage.remove();
        });
        this.showBarrages = [];
        this.stashBarrages = [];
        this.specialBarrages = [];
        this.RuntimeManager.container = [];
        this.RuntimeManager.resize();
        callHook(this.opts.hooks, 'clear', [this]);
      }
    }, {
      key: "clone",
      value: function clone(opts) {
        opts = opts ? Object.assign(this.opts, opts) : this.opts;
        return new this.constructor(opts);
      }
    }, {
      key: "use",
      value: function use(fn) {
        warning(typeof fn === 'function', 'Plugin must be a function.');

        if (this.plugins.has(fn)) {
          return this.plugins.get(fn);
        }

        for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
          args[_key - 1] = arguments[_key];
        }

        var result = fn.apply(void 0, [this].concat(args));
        this.plugins.set(fn, result);
        return result;
      }
    }, {
      key: "assertCapacity",
      value: function assertCapacity(n) {
        var res = n + this.length > this.opts.capacity;

        if (res) {
          callHook(this.opts.hooks, 'capacityWarning', [this]);
          console.warn("The number of barrage is greater than \"".concat(this.opts.capacity, "\"."));
        }

        return res;
      }
    }, {
      key: "renderBarrage",
      value: function renderBarrage() {
        var _this3 = this;

        if (this.stashBarrages.length > 0) {
          var _this$RuntimeManager = this.RuntimeManager,
              rows = _this$RuntimeManager.rows,
              rowGap = _this$RuntimeManager.rowGap;
          var length = this.opts.limit - this.showLength;

          if (rowGap > 0 && length > rows) {
            length = this.RuntimeManager.rows;
          }

          if (this.opts.forceRender || length > this.stashBarrages.length) {
            length = this.stashBarrages.length;
          }

          if (length > 0 && this.runing) {
            timeSlice(length, function () {
              var currentBarrage = _this3.stashBarrages.shift();

              if (callHook(_this3.opts.hooks, 'willRender', [_this3, currentBarrage, false]) !== false) {
                var needStop = _this3.initSingleBarrage(currentBarrage.data, currentBarrage.hooks);

                if (needStop) {
                  return false;
                }
              }
            });
            callHook(this.opts.hooks, 'render', [this]);
          }
        }
      }
    }, {
      key: "initSingleBarrage",
      value: function initSingleBarrage(data, hooks) {
        var _this4 = this;

        var barrage = data instanceof Barrage ? data : this.createSingleBarrage(data, hooks);
        var newBarrage = barrage && this.sureBarrageInfo(barrage);

        if (newBarrage) {
          newBarrage.append();
          this.showBarrages.push(newBarrage);
          newBarrage.trajectory.values.push(newBarrage);
          this.RuntimeManager.move(newBarrage, this).then(function () {
            newBarrage.destroy();

            if (_this4.length === 0) {
              callHook(_this4.opts.hooks, 'ended', [_this4]);
            }
          });
        } else {
          this.stashBarrages.unshift(barrage);
          return true;
        }
      }
    }, {
      key: "createSingleBarrage",
      value: function createSingleBarrage(data, hooks) {
        var _this$opts$times = _slicedToArray(this.opts.times, 2),
            max = _this$opts$times[0],
            min = _this$opts$times[1];

        var time = Number(max === min ? max : (Math.random() * (max - min) + min).toFixed(0));
        if (time <= 0) return null;
        return new Barrage(data, hooks, time, this, Object.assign({}, this.opts.hooks, {
          barrageCreate: this.setBarrageStyle.bind(this)
        }));
      }
    }, {
      key: "sureBarrageInfo",
      value: function sureBarrageInfo(barrage) {
        var trajectory = this.RuntimeManager.getTrajectory();
        if (!trajectory) return null;
        barrage.trajectory = trajectory;
        barrage.position.y = trajectory.gaps[0];
        return barrage;
      }
    }, {
      key: "setBarrageStyle",
      value: function setBarrageStyle(barrage, node) {
        var _this$opts = this.opts,
            _this$opts$hooks = _this$opts.hooks,
            hooks = _this$opts$hooks === void 0 ? {} : _this$opts$hooks,
            direction = _this$opts.direction;
        node.style.opacity = 0;
        node.style[direction] = 0;
        node.style.position = 'absolute';
        node.style.display = 'inline-block';
        node.style.pointerEvents = this.isShow ? 'auto' : 'none';
        node.style.visibility = this.isShow ? 'visible' : 'hidden';
        callHook(hooks, 'barrageCreate', [barrage, node]);
      }
    }, {
      key: "stashLength",
      get: function get() {
        return this.stashBarrages.length;
      }
    }, {
      key: "specialLength",
      get: function get() {
        return this.specialBarrages.length;
      }
    }, {
      key: "showLength",
      get: function get() {
        return this.showBarrages.length + this.specialBarrages.length;
      }
    }, {
      key: "length",
      get: function get() {
        return this.showBarrages.length + this.specialBarrages.length + this.stashBarrages.length;
      }
    }, {
      key: "containerWidth",
      get: function get() {
        return this.RuntimeManager.containerWidth;
      }
    }, {
      key: "containerHeight",
      get: function get() {
        return this.RuntimeManager.containerHeight;
      }
    }, {
      key: "runing",
      get: function get() {
        return this.loopTimer !== null;
      }
    }]);

    return BarrageManager;
  }();

  function createBarrageManager() {
    var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    opts = Object.assign({
      hooks: {},
      limit: 100,
      height: 50,
      rowGap: 50,
      isShow: true,
      capacity: 1024,
      times: [5, 10],
      interval: 1,
      direction: 'right',
      forceRender: false
    }, opts);
    return new BarrageManager(opts);
  }

  var index = {
    Timeline: Timeline,
    create: createBarrageManager
  };

  return index;

}));
